/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ca.usask.cs.decisiontree.XMLHandler;

import ca.usask.cs.Startup.Main;
import ca.usask.cs.decisiontree.*;
import ca.usask.cs.decisiontree.api.DecisionTree;
import ca.usask.cs.decisiontree.api.EventEdgeInterface;
import ca.usask.cs.decisiontree.api.EventNodeInterface;
import ca.usask.cs.decisiontree.api.Edge;
import ca.usask.cs.decisiontree.api.IntermediateTreeNode;
import ca.usask.cs.decisiontree.api.TerminalNodeInterface;
import ca.usask.cs.decisiontree.api.Node;

import java.util.HashMap;
import java.util.Iterator;
import org.w3c.dom.Element;


/**
 * 
 * @author tonyleung
 * @editor Wenyi AN
 */
public class XMLController
{

    private HashMap treeNodeList;
    private TreeStructureHandler treeStructure;
   // private Main mainController;
    WriteXML write;
    LoadXML load;
    
    public XMLController(Main theMainController)
    {
        this.treeStructure = theMainController.getTreeSturcture();
        initXML();
    }
    
    public void initXML(){
    	treeNodeList = new HashMap();
        // creates loadXML with the specified file path
        load = new LoadXML(this);
        write = new WriteXML();
    }

    /**
     * loads the xml file specified by the constructor
     */
    public void load(String theFileName)
    {
        String fileName = theFileName;
        //parse the xml file and get the dom object
		this.load.setup(fileName);
        // parses the tree
        load.parse();
    }

    /**
     * writes the xml file specified by the constructor
     */
    public void write(String theFileName)
    {
        String fileName = theFileName;
        Element test = write.setup();
        writeBasicDecisionTree(test);
        write.printToFile(fileName);
    }

    /**
     * Creates a DecisionTree with the specified information
     *      uses the rootNodeID to retrieve the treeNode from treeNodeList
     * @param treeNodeID
     * @param treeName
     * @param rootNodeID
     */
    public void createDecisionTree(String treeType, int treeNodeID, String treeName, int rootNodeID)
    {
        IntermediateTreeNode rootNode;
        rootNode = (IntermediateTreeNode) this.treeNodeList.get(rootNodeID);
        DecisionTree tree = this.treeStructure.createBasicDecisionTree(rootNode, treeNodeID);
        tree.setTreeName(treeName);
    }

    /**
     * Uses the given information to create the corresponding treeNode
     * Then adds the TreeNode to the TreeNodeList using the ID as the key
     * @param type - the type of TreeNode (ChanceNode, DecisionNode, TerminalNode)
     * @param ID - the treeNodeID of the TreeNode
     * @param time - time of the TreeNode
     * @param varName - variableName of a IntermediateTreeNodes
     * @param termValue - terminalValue  of a TerminalNode
     * @param selected - selected  of the TreeNode
     */
    public void createTreeNode(String type, int ID, double time, String varName, double termValue, boolean selected)
    {
        Node newNode = null;
//    	IntermediateTreeNode newIntNode;
//        TerminalNodeInterface newTerNode;
       // check the type of TreeNode and call their corresponding creation method
        if(type.compareToIgnoreCase("ChanceNode") == 0)
        {
        	newNode = this.treeStructure.createChanceNode(ID);
    //        setNodeInfo(newNode,ID, time, varName,selected,termValue);
        }
        else if(type.compareToIgnoreCase("DecisionNode") == 0)
        {
        	newNode = this.treeStructure.createDecisionNode(ID);
     //       setNodeInfo(newNode,ID, time, varName,selected,termValue);
        }
        else if(type.compareToIgnoreCase("TerminalNode") == 0)
        {
        	newNode = this.treeStructure.createTerminalNode(ID);
        }
        setNodeInfo(newNode,ID, time,varName,selected,termValue);
    }
    /**
     * set Node information
     * @param newNode
     * @param ID
     * @param time
     * @param varName
     * @param selected
     * @param termValue
     */
    public void setNodeInfo(Node newNode,int ID, double time, String varName,boolean selected,double termValue){
    	newNode.setSelected(selected);
    	newNode.setRollBackValue(termValue);
    	newNode.setTime(time);
    	
//    	if(newNode.getNodeType().equals("TerminalNode"))    // originally, this was restricted to Terminal Nodes, but this caused bugs with missing variable names when loading xml files
   		newNode.setVariableName(varName);

    	this.treeNodeList.put(ID, newNode);
//        System.out.println("the size of XMLlist is "+treeNodeList.size());
    }
  
    /**
     * Creates the specified Edge using the given information. uses the SourceID
     * and destID to lookup the corresponding TreeNodes to create the Edge. Appends
     * the Edge to the sourceNode
     * @param type - type of the Edge
     * @param ID - edgeID of the Edge
     * @param value - value of the Edge
     * @param prob - probability of a ChanceEdge
     * @param selected - selected of the Edge
     * @param sourceID - The treeNodeID of the sourceTreeNode
     * @param destID = the treeNodeID of the destinationTreeNode
     */
    public void createEdge(String type, int ID, double value, double prob, boolean selected, int sourceID, int destID,String name)
    {
        EventEdgeInterface newChanceEdge;
        Edge newBasicEdge;
        IntermediateTreeNode sourceNode;
        Node destNode;
        sourceNode = (IntermediateTreeNode)this.treeNodeList.get(sourceID);
        destNode = (Node)this.treeNodeList.get(destID);
        
		// check the type of TreeNode and call their corresponding creation method
        if(type.compareToIgnoreCase("ChanceEdge") == 0)
        {
            newChanceEdge = this.treeStructure.createChanceEdge(sourceNode, destNode, ID,name);
            setChanceEdge(newChanceEdge,destNode,prob,value,sourceNode);
           
        }
        else if(type.compareToIgnoreCase("BasicEdge") == 0)
        {
            newBasicEdge = this.treeStructure.createBasicEdge(sourceNode, destNode, ID,name);
            setBasicEdge(newBasicEdge,destNode,selected,value,sourceNode);
           
        }
    }
    
    public void setChanceEdge(EventEdgeInterface newChanceEdge,Node destNode,double prob,double value,IntermediateTreeNode sourceNode){
    	 this.treeStructure.setCurrentEdge(newChanceEdge);
         this.treeStructure.setCurrentNode(destNode);
         this.treeStructure.setSourceEdge(newChanceEdge);
         newChanceEdge.setProbability(prob);
         newChanceEdge.setValue(value);
         this.treeStructure.setCurrentNode(sourceNode);
         this.treeStructure.addChildEdge(newChanceEdge, prob);
    }
    
    public void setBasicEdge(Edge newBasicEdge,Node destNode,boolean selected,double value,IntermediateTreeNode sourceNode){
    	 this.treeStructure.setCurrentEdge(newBasicEdge);
         this.treeStructure.setCurrentNode(destNode);
         this.treeStructure.setSourceEdge(newBasicEdge);
         newBasicEdge.setSelected(selected);
         newBasicEdge.setValue(value);
         this.treeStructure.setCurrentNode(sourceNode);
         this.treeStructure.addChildEdge(newBasicEdge);
    }
    /**
     * Creates the specified Edge using the given information. uses the SourceID
     * and destID to lookup the corresponding TreeNodes to create the Edge. Appends
     * the Edge to the sourceNode
     * @param type - type of the Edge
     * @param ID - edgeID of the Edge
     * @param value - value of the Edge
     * @param prob - probability of a ChanceEdge
     * @param selected - selected of the Edge
     * @param sourceID - The treeNodeID of the sourceTreeNode
     * @param destID = the treeNodeID of the destinationTreeNode
     */
    public void createNewEdge(String type, int ID, double value, double prob, boolean selected, Node surNode, Node destNode,String name)
    {
        EventEdgeInterface newChanceEdge;
        Edge newBasicEdge;
        Node sourceNode=surNode;
      
        
        // check the type of TreeNode and call their corresponding creation method
        if(type.compareToIgnoreCase("ChanceEdge") == 0)
        {
            newChanceEdge = this.treeStructure.createChanceEdge(sourceNode, destNode, ID , name);
            setNewChanceEdge(newChanceEdge,destNode,prob,value,sourceNode);
            this.treeStructure.setCurrentNode(destNode);
        }
        else if(type.compareToIgnoreCase("BasicEdge") == 0)
        {
            newBasicEdge = this.treeStructure.createBasicEdge(sourceNode, destNode, ID, name);
            setNewBasicEdge(newBasicEdge,destNode,selected,value,sourceNode);
            this.treeStructure.setCurrentNode(destNode);
        }
    }
    public void setNewChanceEdge(EventEdgeInterface newChanceEdge,Node destNode,double prob,double value,Node sourceNode){
   	 this.treeStructure.setCurrentEdge(newChanceEdge);
        this.treeStructure.setCurrentNode(destNode);
        this.treeStructure.setSourceEdge(newChanceEdge);
        newChanceEdge.setProbability(prob);
        newChanceEdge.setValue(value);
        this.treeStructure.setCurrentNode(sourceNode);
        this.treeStructure.addChildEdge(newChanceEdge, prob);
   }
    public void setNewBasicEdge(Edge newBasicEdge,Node destNode,boolean selected,double value,Node sourceNode){
   	 this.treeStructure.setCurrentEdge(newBasicEdge);
        this.treeStructure.setCurrentNode(destNode);
        this.treeStructure.setSourceEdge(newBasicEdge);
        newBasicEdge.setSelected(selected);
        newBasicEdge.setValue(value);
        this.treeStructure.setCurrentNode(sourceNode);
        this.treeStructure.addChildEdge(newBasicEdge);
   }
    /**
     * Creates a BasicDecisionTree and appends to the curEle. Grabs the tree
     * info from the mainController and sends it to the CreateBasicDecisionTree
     * metho. Then calls the writeTreeNode method to write the treeNodes
     * @param curEle the current element to be appended on
     */
    private void writeBasicDecisionTree(Element curEle)
    {
        DecisionTree tree = this.treeStructure.getDecisionTree();
        String treeName = tree.getTreeName();
        int treeID = tree.getTreeID();

        Element treeEle = this.write.CreateBasicDecisionTree(curEle, treeName, treeID);
        writeTreeNode(treeEle, tree.getRootNode());
    }

    /**
     * Depending on which type of node it is, retrieve the information and call its
     * corresponding creation function.
            * if it is an intermediateNode, loop through each Edge and call writeEdge
     * @param curEle the element that the node appends on
     * @param curNode The current node to be created
     */
    private void writeTreeNode(Element curEle, Node thCurNode)
    {
        // gather all the information to create the corresponding node type and call the its element creation method
        if(thCurNode instanceof TerminalNode)
        {
            int sourceID = -1;
            TerminalNodeInterface curNode = (TerminalNodeInterface) thCurNode;

            // only retrieve sourceNodeID if it is not the rootNode
            if(thCurNode != this.treeStructure.getRootNode())
            {
                sourceID = curNode.getSourceEdge().getEdgeID();
            }

            int treeNodeID = curNode.getTreeNodeID();
            double time = curNode.getTime();
            String varName = curNode.getVariableName();
            double terVal = curNode.getRollBackValue();
            boolean selected = curNode.isSelected();

            this.write.CreateTerminalNodeElement(curEle, treeNodeID, time, varName, terVal, selected, sourceID);
        }
        else if(thCurNode instanceof EventNode)
        {
            int sourceID = -1;

            EventNodeInterface curNode = (EventNodeInterface) thCurNode;

            // only retrieve sourceNodeID if it is not the rootNode
            if(thCurNode != this.treeStructure.getRootNode())
            {
                sourceID = curNode.getSourceEdge().getEdgeID();
            }

            int treeNodeID = curNode.getTreeNodeID();
            double time = curNode.getTime();
            String varName = curNode.getVariableName();
            boolean selected = curNode.isSelected();
            double rollBackValue = curNode.getRollBackValue();

            Element nodeEle = this.write.CreateChanceNodeElement(curEle, treeNodeID, time, varName, rollBackValue, selected, sourceID);

            Iterator<Edge> edgeList = curNode.getChildEdgeList();

            // if there is atleast one edge, append the ChildrenEdgeList element
            if(edgeList.hasNext())
            {

                Element childrenEdgeListEle = this.write.createChildrenEdgeList(nodeEle);

                // for each Edge call writeEdge method
                while(edgeList.hasNext())
                {
                    writeEdge(childrenEdgeListEle, edgeList.next());
                }
            }
            edgeList = curNode.getChildEdgeList();

            // if there is atleast one edge, append the ChildList element
            if(edgeList.hasNext())
            {

                Element childListEle = this.write.createChildList(nodeEle);

                // for each edge call the writeTreeNode with its destinationNode
                while(edgeList.hasNext())
                {
                    writeTreeNode(childListEle, edgeList.next().getDestinationNode());
                }
            }
        }
        else
        {
            int sourceID = -1;

            IntermediateTreeNode curNode = (IntermediateTreeNode) thCurNode;

            // only retrieve sourceNodeID if it is not the rootNode
            if(thCurNode != this.treeStructure.getRootNode())
            {
                sourceID = curNode.getSourceEdge().getEdgeID();
            }
            
            int treeNodeID = curNode.getTreeNodeID();
            double time = curNode.getTime();
            String varName = curNode.getVariableName();
            boolean selected = curNode.isSelected();
            double rollBackValue = curNode.getRollBackValue();
            Edge bestChild = ((DecisionNode) curNode).getBestChildEdge();
            
            String strBestChildNameOrNull = null;
            int idBestChildOrMinusOne = -1;
            
            if (bestChild != null)
            {
                strBestChildNameOrNull = bestChild.getEdgeName();
                idBestChildOrMinusOne = bestChild.getEdgeID();
            }
            Element nodeEle = this.write.CreateDecisionNodeElement(curEle, treeNodeID, time, varName, rollBackValue, selected, sourceID, strBestChildNameOrNull, idBestChildOrMinusOne );

            Iterator<Edge> edgeList = curNode.getChildEdgeList();

            // if there is atleast one edge, append the ChildrenEdgeList element
            if(edgeList.hasNext())
            {

                Element childrenEdgeListEle = this.write.createChildrenEdgeList(nodeEle);

                // for each Edge call writeEdge method
                while(edgeList.hasNext())
                {
                    writeEdge(childrenEdgeListEle, edgeList.next());
                }
            }
            edgeList = curNode.getChildEdgeList();

            // if there is atleast one edge, append the ChildList element
            if(edgeList.hasNext())
            {

                Element childListEle = this.write.createChildList(nodeEle);

                // for each edge call the writeTreeNode with its destinationNode
                while(edgeList.hasNext())
                {
                    writeTreeNode(childListEle, edgeList.next().getDestinationNode());
                }
            }
        }

    }

    public void writeEdge(Element curEle, Edge theCurEdge)
    {
        // depending on type, grab all of its info and call its corresponding creation method
        if(theCurEdge.getEdgeType().equals("ChanceEdge"))
        {
            EventEdge curEdge = (EventEdge) theCurEdge;

            int edgeID = curEdge.getEdgeID();
            String edgeName=curEdge.getEdgeName();
            double val = curEdge.getValue();
            double prob = curEdge.getProbability();
            boolean selected = curEdge.isSelected();
            int sourceID = curEdge.getSourceNode().getTreeNodeID();
            int destID = curEdge.getDestinationNode().getTreeNodeID();

            Element edgeEle = this.write.CreateChanceEdgeElement(curEle, edgeID,edgeName, val, prob, selected, sourceID, destID);
        }
        else
        {
            Edge curEdge = theCurEdge;
            int edgeID = curEdge.getEdgeID();
            String edgeName=curEdge.getEdgeName();
            double val = curEdge.getValue();
            boolean selected = curEdge.isSelected();
            int sourceID = curEdge.getSourceNode().getTreeNodeID();
            int destID = curEdge.getDestinationNode().getTreeNodeID();

            Element edgeEle = this.write.CreateBasicEdgeElement(curEle, edgeID, edgeName, val, selected, sourceID, destID);
        }
    }
    
    public HashMap getTreeNodeList(){
    //	System.out.println("the size of XMLlist is "+treeNodeList.size());
    	return treeNodeList;
    }
}
